控制反转(IoC)
1. IoC的原型
以前写ServiceImpl层调用DaoImpl层,代码如下
package org.gs.service;
import org.gs.dao.UserDao;
import org.gs.dao.UserDaoImpl;
/**
* @author admin
* @date 2021/9/12 2:21 下午
*/
public class UserServiceImpl implements UserService{
private UserDao userDao = new UserDaoImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
其中需要调用到UserDao的实现类代码
private UserDao userDao = new UserDaoImpl();
在程序中是固定的,程序主动创建对象,如要调换其他实现类,则牵一发而动全身。
解决方法:
使用setter等方法(但是在Spring中只能使用setter方法进行对象注入,所以必须要有对应属性的公共权限的setter方法)动态放入实现类,将控制权交给其他人。
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
在以上代码中,程序不再具有主动性,而变成了被动的接收对象。由用户来选择调用什么实现类。
这种思想从本质上解决了问题,程序员不用再去管理对象的创建,同时系统的耦合性大大降低。可以更加专注在业务的实现方面。这就是IoC的原型。
2. IoC的本质
IoC是一种设计思想,DI(依赖注入)是实现IoC的一种方法。
控制反转就是获得依赖对象的控制权反转了。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
3. 开始
准备的dao和service类如下
package org.gs.dao; /** * @author admin * @date 2021/9/12 2:20 下午 */ public class UserDaoImpl implements UserDao{ @Override public void getUser() { System.out.println("默认获取用户的数据"); } }
package org.gs.service; import org.gs.dao.UserDao; /** * @author admin * @date 2021/9/12 2:21 下午 */ public class UserServiceImpl implements UserService{ private UserDao userDao; // Spring通过setter方法进行依赖注入,所以必须要有公共权限的setter方法 public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void getUser() { userDao.getUser(); } }
在res资源目录下创建beans.xml配置文件。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="org.gs.dao.UserDaoImpl" name="userDao" /> <bean class="org.gs.service.UserServiceImpl" name="userService"> <!--name:要注入对象的属性名 ref:引用Spring管理的bean进行注入--> <property name="userDao" ref="userDao"/> <!--value:非引用类型的注入--> <!--<property name="" value=""/>--> </bean> </beans>
测试
@Test public void Test() { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); UserService userService = (UserService) context.getBean("userService"); // 或者通过反射指定对象的类型,避免强转 // UserService userService = context.getBean("userService", UserService.class); userService.getUser(); }
通过上述的代码可以发现,xml中配置的对象以及对象中的属性都是由Spring创建、设置和管理的,这个过程就是控制反转。将控制权交给Spring,由Spring进行对象的管理。程序本身不创建对象,而编程被动的接收对象。
值得注意的是,Spring中的参数注入是使用set方法进行注入的,所以需要有公共权限的setter方法
在Spring中配置的对象,可以通过xml文件进行管理,而基本不需要动Java代码,实现解耦。
4. IoC创建对象的方式
注意点:
Spring在配置文件加载的时候,容器中管理的对象就已经被初始化了,非懒加载。
Spring中管理的对象,默认是单例模式。也就是说get同一个对象的时候拿到的是同一个实例。
Spring默认使用无参构造器创建对象。
假设要使用有参构造器创建对象,有以下方法:
如果bean定义的构造函数参数中不存在潜在的歧义,则bean定义中构造函数参数的顺序是bean实例化时将这些参数提供给适当构造函数的顺序。
package x.y; public class ThingOne { public ThingOne(ThingTwo thingTwo, ThingThree thingThree) { // ... } }
<beans> <bean id="beanOne" class="x.y.ThingOne"> <constructor-arg ref="beanTwo"/> <constructor-arg ref="beanThree"/> </bean> <bean id="beanTwo" class="x.y.ThingTwo"/> <bean id="beanThree" class="x.y.ThingThree"/> </beans>
通过下标赋值(从0开始)
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg index="0" value="7500000"/> <constructor-arg index="1" value="42"/> </bean>
通过类型赋值
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg type="int" value="7500000"/> <constructor-arg type="java.lang.String" value="42"/> </bean>
通过属性名赋值
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg name="years" value="7500000"/> <constructor-arg name="ultimateAnswer" value="42"/> </bean>